﻿#include "protocol/serial.h"
#include "utils/utils.h"
#include "utils/FILEWR.h"
#include <string.h>

#define COM_NAME_SP " "
int serial_max_frame_size = 8;

Serial::Serial(){
    b_direct = false;
    b_serial_open = false;
    port = new QextSerialPort();
    qRegisterMetaType<QByteArray>("QByteArray&");
    qRegisterMetaType<RxMessage>("RxMessage&");
    worker = new QThread();
    decoder = new Decoder(4096);
    timer = new QTimer();
    timer->setSingleShot(false);
    this->moveToThread(worker);
    timer->moveToThread(worker);
    port->moveToThread(worker);
    decoder->moveToThread(worker);
    connect(worker, &QThread::started, this, [this](){
        timer->setInterval(1);
        timer->start();
        qDebug()<<"Timer start";
    });
    connect(decoder, &Decoder::on_frame, this, &Serial::onRecvFrame /*, Qt::DirectConnection*/); //在serial线程中直接调用，如果改为其他参数，接收不到信号
    connect(port, &QextSerialPort::readyRead,this, &Serial::readPort);
    connect(port, &QextSerialPort::aboutToClose,this, &Serial::about_close);
    connect(this, &Serial::sendFrame, this, &Serial::writePort, Qt::QueuedConnection);
    connect(timer, &QTimer::timeout, this, &Serial::onTimerOut);
    connect(this, &Serial::closePort, this, [this](){
        ClosePortInt();
    });
    connect(this, &Serial::openPort, this, [this](){
        OpenPortInt();
    });
    worker->start();
}

QStringList Serial::GethwPorts(){
    QMap<QString, QString> ports = FindComputerCom::enumLocalCom();
    QStringList names;
    for (QMap<QString, QString>::Iterator itor = ports.begin(); itor != ports.end(); ++itor){
        if (itor.value().contains("CH340")) {
            names.insert(0, itor.value() + COM_NAME_SP + itor.key());
        }else {
            names.append(itor.value() + COM_NAME_SP + itor.key());
        }
    }
    return names;
}

void Serial::ClosePort(){
    emit closePort();
}

void Serial::ClosePortInt(){
    if (port->isOpen()) {
        port->close();
        b_serial_open = false;
        emit portStatusChanged(false);
    }
}

bool Serial::OpenPort(QString name, int band, bool direct){
    QStringList coms = name.split(COM_NAME_SP);
    QString com = coms.last();
    if (com.isEmpty()) {
        return false;
    }
    com_name = com;
    band_rate = band;
    direction = direct;

    emit openPort();

    return true;
}

bool Serial::OpenPortInt(){

    ClosePortInt();

    if(band_rate == 9600) {
        port->setBaudRate(BAUD9600);
    }else if (band_rate==115200) {
        port->setBaudRate(BAUD115200);
    }else if (band_rate==38400) {
        port->setBaudRate(BAUD38400);
    }else if (band_rate==500000) {
        port->setBaudRate(BAUD500000);
    }else {
        FILEWARE::error("The baud rate is incorrect","Warning");
        return false;
    }
    port->setTimeout(-1);
    port->setPortName(com_name);
    port->setQueryMode(QextSerialPort::EventDriven);
    if(port->open(QIODevice::ReadWrite))
    {
        port->setTimeout(-1);
        DirectConnect(direction);
        b_serial_open = true;
        emit portStatusChanged(true);
        return true;
    }
    return false;
}

void Serial::DirectConnect(bool direct) {
    b_direct = direct;
    if (b_direct) {
        serial_max_frame_size = 8;
    }else {
        serial_max_frame_size = 8;
    }
}


void Serial::onTimerOut(){
    if (port->isOpen()) {
        if (port->bytesAvailable() == -1) {
            ClosePortInt();
            int trycount = 2;
            while(trycount-- >= 0) {
                QThread::msleep(2000);
                if (OpenPortInt()){
                    break;
                }
            }
        }

    }else if (b_serial_open) {
        b_serial_open = false;
        emit portStatusChanged(false);
    }
    if (port->isOpen()) {
        onResendTimeOut();
    }
}

void Serial::writePort(){
    if (!queue.is_empty()) {
        Command *command = queue.dequeue();
        qDebug() << QDateTime::currentDateTimeUtc() << "Comand: " + Utils::ByteArrayToHexString(command->byte_array());
        //qDebug() << "Command:" + QString::number(command->byte_array()[2], 16);
        qint64 time = command->onPrepare();
        qDebug()<<"Time:" << time << ", " << Utils::timestamp_msec();
        if (time >= 0) {
            qDebug() << "Queue comd";
            wait_queue.enqueue_tail(command);
            if (port->isOpen() && time <= Utils::timestamp_msec()) {
                qDebug() << "send Frame";
                port->write(command->get_buffer());
            }
        }else if (!command->is_async()){
            command->notify_finish();
        }
    }
}


void Serial::readPort(){
    QByteArray data = port->readAll();
    if (!data.isEmpty()) {
        qDebug() << "recv raw:" << QDateTime::currentDateTimeUtc()<< Utils::ByteArrayToHexString(data);
        decoder->append_data((u8 *)data.data(), data.size());
    }
}

void Serial::about_close(){
    qDebug() << "about_close";
}

void Serial::onRecvPackage(RxMessage *message) {
    Command *temp, *node;
    bool is_matched = false;
    //qDebug() << "recv Key: 0x" << QString::number(message->getKey(), 16);
    //qDebug() << "recv Frame:" << QDateTime::currentDateTimeUtc()<< Utils::ByteArrayToHexString(message->byte_array());
    wait_queue.lockqueue();
    for (node = wait_queue.head()->next, temp = node->next; node != wait_queue.head(); node=temp, temp = node->next) {
        if (node->is_match(message)) {
            wait_queue.removeOneLocked(node);
            if (!node->is_async()) {
                node->notify_finish();
                is_matched = true;
            }
        }
    }
    wait_queue.unlockqueue();
    if (!is_matched) {
        //qDebug() << "recv Raw:" << QDateTime::currentDateTimeUtc()<< Utils::ByteArrayToHexString(frame);
        RxMessage msg(message);
        emit frameReady(msg);
    }
}

void Serial::onRecvFrame(QByteArray &frame){
    qDebug() << "recv Frame:" << QDateTime::currentDateTimeUtc()<< Utils::ByteArrayToHexString(frame);
    if (frame.size() < (int)sizeof(head_t)) {
        return;
    }
    RxMessage *message = new RxMessage((u8*)frame.data(), frame.size());

    onRecvPackage(message);
}

void Serial::Send(Command *c, bool just_wait){
    if (just_wait) {
        wait_queue.enqueue_tail(c);
        return;
    }
    //qDebug()<< QDateTime::currentDateTimeUtc() << ":send data\n";
    queue.enqueue(c);
    emit sendFrame();
}

void Serial::onResendTimeOut(){
    Command *temp, *node;
    wait_queue.lockqueue();
    for (node = wait_queue.head()->next, temp = node->next; node != wait_queue.head(); node=temp, temp = node->next) {
        if (node->time <= Utils::timestamp_msec()) {
            wait_queue.removeOneLocked(node);
            if (node->is_expired()) {
                if(!node->is_async()) {
                    node->notify_finish();
                }
            }else {
                Send(node); //resend
            }
        }
    }
    wait_queue.unlockqueue();
}

void CQueue::enqueue(Command *data) {
    QMutexLocker lock(&this->lock);
    Command *node;
    for (node = Head.next; node != &Head; node = node->next) {
        if (node->time > data->time) {
            break;
        }
    }
    data->prev = node->prev;
    node->prev->next = data;
    node->prev = data;
    data->next = node;
    Cond.wakeOne();
}

void CQueue::enqueue_tail(Command *data){
    QMutexLocker lock(&this->lock);
    Command *prev = Head.prev;
    Command *next = &Head;
    next->prev = data;
    data->next = next;
    data->prev = prev;
    prev->next = data;
    Cond.wakeOne();
}

Command* CQueue::dequeue() {
    QMutexLocker lock(&this->lock);
    while(Head.next == &Head) {
        Cond.wait(&this->lock);
    }
    Command *c = Head.next;
    removeOneLocked(c);
    return c;
}

void CQueue::removeOneLocked(Command *c){
    c->prev->next = c->next;
    c->next->prev = c->prev;
    c->next = c->prev = c;
}

void CQueue::removeOne(Command *c){
    QMutexLocker lock(&this->lock);
    removeOneLocked(c);
}
